/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.ui.popup;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.daikit.daikit4gxt.client.DkBaseMain;
import com.daikit.daikit4gxt.client.action.DkBaseAsyncCallback;
import com.daikit.daikit4gxt.client.action.DkBasePagingLoader;
import com.daikit.daikit4gxt.client.log.DkLogger;
import com.daikit.daikit4gxt.client.ui.UIInvalidatable;
import com.sencha.gxt.core.client.IdentityValueProvider;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.loader.BeforeLoadEvent;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.widget.core.client.Dialog;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.BoxLayoutContainer.BoxLayoutPack;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.grid.CheckBoxSelectionModel;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;


public abstract class DkReferenceSelectorPopup<B extends Serializable> implements UIInvalidatable
{

	protected DkLogger log = DkLogger.getLog(getClass());

	protected final Dialog dialogBox;
	protected final ListStore<B> pagingStore;

	protected final String OK = "ok";
	protected final String RESET = "reset";
	protected final String CANCEL = "cancel";

	private final int DEFAULT_POPUP_WIDTH = 1270;
	private final int DEFAULT_POPUP_HEIGHT = 600;

	private TextButton buttonCancel;
	private TextButton buttonOk;
	private TextButton buttonReset;

	private final CheckBoxSelectionModel<B> sm;

	private final DkBasePagingLoader<B, PagingLoadConfig, PagingLoadResult<B>> basePagingLoader;

	private final Set<B> selection = new HashSet<B>();

	private MyReferenceSelectorPopupListener<B> currentListener;

	protected DkReferenceSelectorPopup(final ModelKeyProvider<B> modelKeyProvider)
	{
		dialogBox = new Dialog()
		{
			@Override
			public void hide()
			{
				DkReferenceSelectorPopup.this.onClose();
				super.hide();
			}

			@Override
			protected void createButtons()
			{

				buttonOk = new TextButton(DkBaseMain.i18n().label_ok(), new SelectEvent.SelectHandler()
				{
					@Override
					public void onSelect(final SelectEvent event)
					{
						DkReferenceSelectorPopup.this.onButtonOkClicked();
					}
				});
				buttonOk.setItemId(OK);
				buttonOk.setIcon(DkBaseMain.icons().ok_16());
				addButton(buttonOk);

				buttonReset = new TextButton(DkBaseMain.i18n().label_reset(), new SelectEvent.SelectHandler()
				{

					@Override
					public void onSelect(final SelectEvent event)
					{
						DkReferenceSelectorPopup.this.onButtonResetClicked();
					}

				});
				buttonReset.setItemId(RESET);
				buttonReset.setIcon(DkBaseMain.icons().refresh_16());
				addButton(buttonReset);

				buttonCancel = new TextButton(DkBaseMain.i18n().label_cancel(), new SelectEvent.SelectHandler()
				{

					@Override
					public void onSelect(final SelectEvent event)
					{
						DkReferenceSelectorPopup.this.onButtonCancelClicked();
					}

				});
				buttonCancel.setItemId(CANCEL);
				buttonCancel.setIcon(DkBaseMain.icons().cancel_16());
				addButton(buttonCancel);

				setFocusWidget(buttonOk);
			}
		};

		dialogBox.setModal(true);
		dialogBox.setHeadingText(getLabelPopupTitle());
		dialogBox.setWidth(DEFAULT_POPUP_WIDTH);
		dialogBox.setHeight(DEFAULT_POPUP_HEIGHT);
		dialogBox.setButtonAlign(BoxLayoutPack.CENTER);
		dialogBox.setResizable(false);

		final IdentityValueProvider<B> identity = new IdentityValueProvider<B>();

		sm = new CheckBoxSelectionModel<B>(identity);

		final List<ColumnConfig<B, ?>> columnConfigs = new ArrayList<ColumnConfig<B, ?>>(getColumnConfigs());
		columnConfigs.add(0, sm.getColumn());

		pagingStore = new ListStore<B>(modelKeyProvider);
		final ColumnModel<B> cmPaging = new ColumnModel<B>(columnConfigs);
		final Grid<B> gridPaging = new Grid<B>(pagingStore, cmPaging);

		basePagingLoader = new DkBasePagingLoader<B, PagingLoadConfig, PagingLoadResult<B>>(pagingStore, getLoadingLabel(),
				DkBaseMain.REFERENCE_SELECTOR_PAGING_SIZE)
		{
			@Override
			protected void doLoad(final PagingLoadConfig pagingLoadConfig, final DkBaseAsyncCallback<PagingLoadResult<B>> callback)
			{
				callLoadBeansRpc(pagingLoadConfig, callback);
			}
		};
		gridPaging.setLoader(basePagingLoader);
		gridPaging.setSelectionModel(sm);

		basePagingLoader.addBeforeLoadHandler(new BeforeLoadEvent.BeforeLoadHandler<PagingLoadConfig>()
		{
			@Override
			public void onBeforeLoad(final BeforeLoadEvent<PagingLoadConfig> event)
			{
				selection.addAll(sm.getSelectedItems());
			}
		});

		final VerticalLayoutContainer contentPanelLayoutContainer = new VerticalLayoutContainer();
		contentPanelLayoutContainer.add(basePagingLoader.getPagingToolbar(), new VerticalLayoutData(1, -1));
		contentPanelLayoutContainer.add(gridPaging, new VerticalLayoutData(1, 1));
		contentPanelLayoutContainer.setBorders(true);
		dialogBox.add(contentPanelLayoutContainer);
	}

	protected void onClose()
	{
		sm.deselectAll();
		selection.clear();
	}

	protected void onButtonCancelClicked()
	{
		onClose();
		dialogBox.hide();
	}

	protected void onButtonResetClicked()
	{
		sm.deselectAll();
		selection.clear();
	}

	protected void onButtonOkClicked()
	{
		selection.addAll(sm.getSelection());
		if (currentListener != null)
		{
			currentListener.onSelectionOk(new HashSet<B>(selection));
		}
		onClose();
		dialogBox.hide();
	}

	protected void onBeforeShow()
	{
		dialogBox.setHeadingText(getLabelPopupTitle());
		selection.clear();
		sm.deselectAll();
	}

	/**
	 * Show the Dialog box for edition of the given model
	 *
	 * @param listener
	 *           the {@link MyReferenceSelectorPopupListener}
	 */
	public void show(final MyReferenceSelectorPopupListener<B> listener)
	{
		onBeforeShow();
		this.currentListener = listener;
		dialogBox.show();
		basePagingLoader.getReloadAction().execute();
	}

	protected abstract void callLoadBeansRpc(final PagingLoadConfig pagingLoadConfig, final DkBaseAsyncCallback<PagingLoadResult<B>> callback);

	/**
	 * @return the dialog box title
	 */
	protected final String getLabelPopupTitle()
	{
		return DkBaseMain.i18n().popup_reference_selector_title(getBeanLabel());
	}

	/**
	 * @return the loading action progress label
	 */
	protected final String getLoadingLabel()
	{
		return DkBaseMain.i18n().action_load_loading_label(getBeanLabel());
	}

	/**
	 * @return the bean label to be used in dialog box titles / messages / action progress bars ...
	 */
	protected abstract String getBeanLabel();

	protected abstract List<ColumnConfig<B, ?>> getColumnConfigs();

	@Override
	public void invalidateUi()
	{
		final boolean isLogged = DkBaseMain.model().isLogged();
		buttonOk.setEnabled(isLogged);
		buttonCancel.setEnabled(isLogged);
	}

	/**
	 * Update the Dialog box width
	 *
	 * @param popupWidth
	 *           the new width in pixels
	 */
	public void setPopup_width(final int popupWidth)
	{
		dialogBox.setWidth(popupWidth);
	}

	/**
	 * Update the Dialog box height
	 *
	 * @param popupHeight
	 *           the new height in pixels
	 */
	public void setPopup_height(final int popupHeight)
	{
		dialogBox.setHeight(popupHeight);
	}

	/**
	 * Change the selection between multiple and simple. Simple is the default.
	 *
	 * @param isMultiple
	 *           true if selection should be multiple, false if selection should be simple
	 */
	public void setSelectionMultiple(final boolean isMultiple)
	{
		sm.deselectAll();
		sm.setSelectionMode(isMultiple ? SelectionMode.MULTI : SelectionMode.SINGLE);
	}

	/**
	 * Listener for Reference Selector events
	 *
	 * @author tcaselli
	 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
	 * @param <C>
	 */
	public static interface MyReferenceSelectorPopupListener<C>
	{
		/**
		 * Callback when button OK is clicked and selection is added to ListStore
		 *
		 * @param selectedItems
		 */
		void onSelectionOk(final Set<C> selectedItems);
	}
}
